#!/usr/bin/env python3
"""
Pymodbus 3.x Modbus RTU 主站 (客户端) 示例
用于连接到 RTU_m_server.py 模拟的多从站服务器
"""

import time
import logging
from pymodbus.client import ModbusSerialClient

# --- 配置 ---
# !! 重要: 修改此处的串口号以匹配您的系统和连接 !!
# 在Windows上可能是 'COM1', 'COM3' 等
# 在Linux上可能是 '/dev/ttyUSB0', '/dev/ttyS0' 等
# 确保您有权限访问该串口
SERIAL_PORT = 'COM2'  # <--- 在这里修改串口号

BAUDRATE = 9600
PARITY = 'N'
STOPBITS = 1
BYTESIZE = 8
TIMEOUT = 1 # 秒

# 要交互的从站ID
SLAVE_ID_1 = 1
SLAVE_ID_2 = 2

# 读取数据的间隔时间（秒）
READ_INTERVAL = 2

# 配置日志
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.INFO) # 可以设置为 DEBUG 获取更详细的 Pymodbus 日志

def run_rtu_client():
    """运行 Modbus RTU 客户端"""

    log.info("初始化 Modbus RTU 客户端...")
    client = ModbusSerialClient(
        port=SERIAL_PORT,
        baudrate=BAUDRATE,
        parity=PARITY,
        stopbits=STOPBITS,
        bytesize=BYTESIZE,
        timeout=TIMEOUT
        # framer=FramerType.RTU 是 ModbusSerialClient 的默认值，无需显式指定
    )

    try:
        log.info(f"尝试连接到串行端口 {SERIAL_PORT}...")
        if not client.connect():
            log.error(f"无法连接到串行端口 {SERIAL_PORT}。请检查端口名称、权限和连接。")
            return # 连接失败则退出

        log.info("连接成功。开始轮询数据...")

        while True:
            try:
                # --- 读取从站 1 (ID: SLAVE_ID_1) ---
                log.info(f"\n--- 读取从站 {SLAVE_ID_1} ---")

                # 读保持寄存器 (FC=03), 地址 0, 数量 3 (根据服务器设置)
                hr_result_1 = client.read_holding_registers(address=10, count=3, slave=SLAVE_ID_1)
                if not hr_result_1.isError():
                    log.info(f"  保持寄存器 (Addr 0, Count 3): {hr_result_1.registers}")
                else:
                    log.error(f"  读取从站 {SLAVE_ID_1} 保持寄存器失败: {hr_result_1}")

                # 读输入寄存器 (FC=04), 地址 0, 数量 2 (根据服务器设置)
                ir_result_1 = client.read_input_registers(address=10, count=2, slave=SLAVE_ID_1)
                if not ir_result_1.isError():
                    log.info(f"  输入寄存器 (Addr 0, Count 2): {ir_result_1.registers}")
                else:
                    log.error(f"  读取从站 {SLAVE_ID_1} 输入寄存器失败: {ir_result_1}")

                # 读线圈 (FC=01), 地址 0, 数量 2 (根据服务器设置)
                co_result_1 = client.read_coils(address=0, count=2, slave=SLAVE_ID_1)
                if not co_result_1.isError():
                    # co_result_1.bits 列表长度可能大于请求的数量，需要切片
                    log.info(f"  线圈 (Addr 0, Count 2): {co_result_1.bits[:2]}")
                else:
                    log.error(f"  读取从站 {SLAVE_ID_1} 线圈失败: {co_result_1}")

                # 读离散输入 (FC=02), 地址 0, 数量 2 (根据服务器设置)
                di_result_1 = client.read_discrete_inputs(address=0, count=2, slave=SLAVE_ID_1)
                if not di_result_1.isError():
                    # di_result_1.bits 列表长度可能大于请求的数量，需要切片
                    log.info(f"  离散输入 (Addr 0, Count 2): {di_result_1.bits[:2]}")
                else:
                    log.error(f"  读取从站 {SLAVE_ID_1} 离散输入失败: {di_result_1}")

                # --- 读取从站 2 (ID: SLAVE_ID_2) ---
                log.info(f"\n--- 读取从站 {SLAVE_ID_2} ---")

                # 读保持寄存器 (FC=03), 地址 0, 数量 2 (根据服务器设置)
                hr_result_2 = client.read_holding_registers(address=0, count=2, slave=SLAVE_ID_2)
                if not hr_result_2.isError():
                    log.info(f"  保持寄存器 (Addr 0, Count 2): {hr_result_2.registers}")
                else:
                    log.error(f"  读取从站 {SLAVE_ID_2} 保持寄存器失败: {hr_result_2}")

                # 读输入寄存器 (FC=04), 地址 0, 数量 2 (根据服务器设置)
                ir_result_2 = client.read_input_registers(address=0, count=2, slave=SLAVE_ID_2)
                if not ir_result_2.isError():
                    log.info(f"  输入寄存器 (Addr 0, Count 2): {ir_result_2.registers}")
                else:
                    log.error(f"  读取从站 {SLAVE_ID_2} 输入寄存器失败: {ir_result_2}")

                # 读线圈 (FC=01), 地址 0, 数量 2 (根据服务器设置)
                co_result_2 = client.read_coils(address=0, count=2, slave=SLAVE_ID_2)
                if not co_result_2.isError():
                    log.info(f"  线圈 (Addr 0, Count 2): {co_result_2.bits[:2]}")
                else:
                    log.error(f"  读取从站 {SLAVE_ID_2} 线圈失败: {co_result_2}")

                # 读离散输入 (FC=02), 地址 0, 数量 2 (根据服务器设置)
                di_result_2 = client.read_discrete_inputs(address=0, count=2, slave=SLAVE_ID_2)
                if not di_result_2.isError():
                    log.info(f"  离散输入 (Addr 0, Count 2): {di_result_2.bits[:2]}")
                else:
                    log.error(f"  读取从站 {SLAVE_ID_2} 离散输入失败: {di_result_2}")

            except Exception as read_e:
                log.error(f"读取数据时发生错误: {read_e}")
                # 可以在这里添加重连逻辑或直接退出

            # 等待下一个读取周期
            log.info(f"\n等待 {READ_INTERVAL} 秒...")
            time.sleep(READ_INTERVAL)

    except KeyboardInterrupt:
        log.info("接收到中断信号，停止客户端...")
    except Exception as e:
        log.error(f"发生未处理的错误: {e}")
    finally:
        if client.is_socket_open():
            log.info("关闭 Modbus 连接。")
            client.close()

if __name__ == "__main__":
    run_rtu_client()
